﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using ArtMath.Core.Data;
using ArtMath.Component.Point;
using ArtMath.Setting;
using ArtMath.Data;
using ArtMath.Component.Line;
using ArtMath.Resource;
using ArtMath.Attribute;
using ArtMath.Converter;
using ArtMath.PointCollection;
using ArtMath.Core.Helper;
using ArtMath.Component.Manager;
using ArtMath.Helper;
using ArtMath.Component.Annotation;
using ArtMath.Flag;
using ArtMath.Component.Point.Interface;
using ArtMath.CommonType;

namespace ArtMath.Component
{
    [Serializable]
    public abstract class LineBase : ShapeBase
    {
        PadStringX lineWidth;
        ColorX lineColor;
        DashStyle lineDashStyle = Settings.Instance.LineDashStyle;
        string formula;
        PropertyDisplayer formulaDisplayer;
        [Browsable(false)]
        public List<PointBase> OutChildren_Point { get; } = new List<PointBase>();
        [Browsable(false)]
        public List<LineBase> OutChildren_Line { get; } = new List<LineBase>();
        public PointD AttachedLocation;
        public PointBase[] InPoints;
        #region MovePrepare
        protected PointBase[] fixedPoints;//, sortedPoints;
        protected PointD controlPointOriginalCoordinate;
        protected KeyValuePair<PointD, double>[] controlledInPointsOriginalInfo;
        [Browsable(false)]
        public Dictionary<PointBase, double[]> ControlledOutPointsOriginalInfo { get; } = new Dictionary<PointBase, double[]>();
        #endregion
        public LineBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            LineWidth = new PadStringX(Pad, Settings.Instance.LineWidth);
            lineColor = new ColorX(Pad, Settings.Instance.LineColor);
            TrackWidth.SetExpression(LineWidth.Value, false);
            FormulaDisplayer = new PropertyDisplayer(this, "Formula");
        }
        void LineWidth_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (lineWidth.Value > 0)
            {
                ForEachStraightLine((StraightLineBase line) =>
                {
                    line.LineWidth.SetExpression(LineWidth.DirectExpression);
                    return true;
                });
            }
            else
            {
                Pad.ShowMessage(Resources.Translate("LineWidthMustBePositive"), MessageType.Error);
                lineWidth.SetExpression(e.OldExpression, false);
            }
        }
        public override PointD MassPoint
        {
            get
            {
                if (InPoints == null || InPoints.Length == 0) throw new NotSupportedException();
                return Utility.GetBaryCenter(InPoints.Select((PointBase pt) => { return pt.MassPoint; }).ToList());
            }
        }
        [Browsable(false)]
        protected virtual Pen LinePen
        {
            get
            {
                return new Pen(StyleState_ == StyleState.Hover || Selected && Pad.UseReversedColor ? lineColor.ReversedColor : lineColor.Color, Math.Max(LineWidth.FloatValue, float.Epsilon))
                {
                    DashStyle = LineDashStyle,
                    LineJoin = LineJoin.Round
                };
            }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "Formula", "Displayer")]
        public virtual PropertyDisplayer FormulaDisplayer
        {
            get { return formulaDisplayer; }
            set { formulaDisplayer = value; }
        }
        [MultilingualCategory("Appearance"), DefaultValue(typeof(PadStringX), "3"), MultilingualDisplayName("LineWidth")]
        public PadStringX LineWidth
        {
            get { return lineWidth; }
            set
            {
                if (lineWidth is object)
                {
                    lineWidth.ExpressionSet -= LineWidth_ExpressionSet;
                }
                lineWidth = value;
                if (lineWidth is object)
                {
                    lineWidth.ExpressionSet += LineWidth_ExpressionSet;
                }
            }
        }
        [MultilingualCategory("Appearance"), DefaultValue(typeof(Color), "Blue"), MultilingualDisplayName("LineColor")]
        public Color LineColor
        {
            get { return lineColor.Color; }
            set
            {
                lineColor.Color = value;
                ForEachStraightLine((StraightLineBase line) =>
                {
                    line.LineColor = LineColor;
                    return true;
                });
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualCategory("Appearance"), DefaultValue(typeof(DashStyle), "Solid"), MultilingualDisplayName("LineDashStyle"), TypeConverter(typeof(DashStyleConverter))]
        public DashStyle LineDashStyle
        {
            get { return lineDashStyle; }
            set
            {
                lineDashStyle = value;
                ForEachStraightLine((StraightLineBase line) =>
                {
                    line.LineDashStyle = LineDashStyle;
                    return true;
                });
                Pad.Canvas.Invalidate();
            }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("Formula")]
        public virtual string Formula
        {
            get { return formula; }
        }
        public override PointD AnchorPoint(PropertyDisplayer disp)
        {
            if (InPoints != null)
            {
                double xSum = 0, ySum = 0;
                foreach (PointBase item in InPoints)
                {
                    if (item != null)
                    {
                        xSum += item.Pxy.X;
                        ySum += item.Pxy.Y;
                    }
                }
                return new PointD(xSum / InPoints.Length, ySum / InPoints.Length);
            }
            return PointD.Empty;
        }
        public override bool IsIndependent
        {
            get
            {
                if (!base.IsIndependent || OutChildren_Point.Count > 0 || OutChildren_Line.Count > 0) return false;
                if (InPoints != null)
                {
                    foreach (PointBase pt in InPoints)
                    {
                        if (pt == null) continue;
                        foreach (LineBase line in pt.InHosts)
                        {
                            if (line != this)
                            {
                                if (!line.InHosts.Contains(this))
                                {
                                    return false;
                                }
                            }
                        }
                    }
                }
                return true;
            }
        }
        [Browsable(false)]
        public virtual bool IsConnectedLineGroup
        {
            get { return false; }
        }
        public override bool Locked
        {
            get
            {
                if (InPoints == null) return base.Locked;
                foreach (PointBase item in InPoints)
                {
                    if (!item.Locked)
                    {
                        return false;
                    }
                }
                return true;
            }

            set
            {
                ForEachPoint((PointBase pt) =>
                {
                    IFreePoint free = pt as IFreePoint;
                    if (free != null)
                    {
                        free.UserMoveLock = value;
                    }
                    return true;
                });
                base.Locked = value;
            }
        }
        public int CountMovablePoints(bool buildFixedArray = false)
        {
            int sum = 0;
            List<PointBase> fix = buildFixedArray ? new List<PointBase>() : null;
            if (InPoints != null)
            {
                foreach (PointBase item in InPoints)
                {
                    if (item == null) continue;
                    if (item.Movable)
                    {
                        sum++;
                    }
                    else
                    {
                        if (buildFixedArray)
                        {
                            fix.Add(item);
                        }
                    }
                }
            }
            if (buildFixedArray)
            {
                fixedPoints = fix.ToArray();
            }
            return ObjectState == ObjectState.Deleted && InPoints != null ? InPoints.Length : sum;
        }
        public override void InChildrenIterator(Predicate<ShapeBase> act)
        {
            if (act == null) return;
            if (InPoints != null)
            {
                foreach (PointBase pt in InPoints)
                {
                    if (pt == null) continue;
                    if (!act(pt)) break;
                }
            }
        }
        public override void RotateAngleChanged(double oldValue)
        {
            //base.RotateAngleChanged(sender, oldValue);
            if (InPoints != null)
            {
                PointBase fix = null, free1 = null, free2 = null;
                foreach (PointBase item in InPoints)
                {
                    if (item.Movable)
                    {
                        if (free1 == null)
                        {
                            free1 = item;
                        }
                        else if (free2 == null)
                        {
                            free2 = item;
                        }
                    }
                    else
                    {
                        if (fix == null)
                        {
                            fix = item;
                        }
                        else
                        {
                            Pad.ShowMessage(string.Format(Resources.Translate("MoreFixedPointsCannotMove"), Name), MessageType.Exclamation);
                            return;
                        }
                    }
                }
                if (fix != null && !RotateCenter.ToPointD().AlmostEquals(fix.ToPointD(), false) || free1 == null)
                {
                    return;
                }
                double angleDelta = RotateAngle.RadValue - Pad.GetRad(oldValue);
                PointD center = RotateCenter.ToPointD();
                if (!Settings.Instance.SelfRotate)
                {
                    center -= MassPoint - free1.ToPointD();
                }
                PointD dest = free1.ToPointD().RotateAt(center, angleDelta);
                if (Settings.Instance.SelfRotate && free2 != null && fix == null)
                {
                    bool move1 = false;
                    DisableTrack(() =>
                    {
                        move1 = Pad.MovePoint(free1, Pad.XYToPxy(dest));
                    });
                    if (move1)
                    {
                        free1.TempMoveLock = true;
                        dest = free2.ToPointD().RotateAt(free1.ToPointD(), angleDelta);
                        Pad.MovePoint(free2, Pad.XYToPxy(dest));
                        free1.TempMoveLock = false;
                    }
                }
                else
                {
                    Pad.MovePoint(free1, Pad.XYToPxy(dest));
                }
            }
        }
        [Browsable(false)]
        public virtual bool PointsHaveConstraint
        {
            get { return InPoints?.Length > 2; }
        }
        [Browsable(false)]
        public virtual int ClickTimes
        {
            get
            {
                if (InPoints != null)
                {
                    return InPoints.Length;
                }
                return 0;
            }
        }
        public override bool ContainsMovablePoint
        {
            get
            {
                if (InPoints != null)
                {
                    foreach (PointBase item in InPoints)
                    {
                        if (item.Movable)
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
        public override void Relocate(PointsRelocationReason reason)
        {
            //if (InPoints != null)
            //{
            //    foreach (PointBase item in InPoints)
            //    {
            //        if (item == null) continue;
            //        item.Relocate(reason);
            //    }
            //}
            InChildrenIterator((ShapeBase sb) =>
            {
                sb.Relocate(reason);
                return true;
            });
            base.Relocate(reason);
        }
        public override void ForEachPoint(Predicate<PointBase> act)
        {
            if (act == null || InPoints == null) return;
            foreach (PointBase item in InPoints)
            {
                if (item == null) continue;
                if (!act(item))
                {
                    break;
                }
            }
        }
        public virtual PointCollectionBase[] GetInPointMovingRange(PointBase movePoint, Base sender = null)
        {
            if (Flags.Shift && DoAngleFix && InPoints != null && IsTop && Pad.AbsorbedObjects.Contains(movePoint) && CountMovablePoints() >= InPoints.Length - 1)
            {
                PointD fix = fixedPoints?.Length > 0 ? fixedPoints[0].NewXY : PointD.Empty;
                return new PointCollectionBase[] { new Ray() { SourcePoint = fix, DirectedAngle = Utility.GetFixedAngle(fix.GetAngle(movePoint.NewXY)) } };
            }
            return Universal.GenerateInstanceArray();
        }
        public override void ExternalObjectMove(PointBase movePoint, Base sender = null)
        {
            base.ExternalObjectMove(movePoint, sender);
            PointMoving_OutChildrenLimit(movePoint);
        }
        public virtual void InternalPointMove(PointBase movePoint, Base sender = null)
        {
            if (InPoints != null && IsTop)
            {
                int movCnt = CountMovablePoints();
                if (movCnt == InPoints.Length)
                {
                    foreach (PointBase item in InPoints)
                    {
                        if (item != movePoint)
                        {
                            item.Move(item.Pxy + movePoint.NewPxy - movePoint.Pxy, PointsHaveConstraint);
                        }
                    }
                }
                else if (movCnt == InPoints.Length - 1 && movCnt > 1)
                {
                    PointD[] tmp = Utility.Rotate(controlPointOriginalCoordinate,
                        movePoint.NewXY, fixedPoints[0].NewXY, controlledInPointsOriginalInfo);
                    int i = 0;
                    foreach (PointBase item in InPoints)
                    {
                        if (item.Movable && item != movePoint && item != fixedPoints[0])//旗帜形状需要最后一条判断（线段中点为直角三角形的直角点，一个端点为锐角点）
                        {
                            item.Move(Pad.XYToPxy(Utility.Rotate(controlPointOriginalCoordinate,
                        movePoint.NewXY, fixedPoints[0].NewXY, controlledInPointsOriginalInfo[0].Key,
                        controlledInPointsOriginalInfo[0].Value)), PointsHaveConstraint);
                            i++;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 当图形即将发生形变时的任务
        /// </summary>
        /// <param name="movePoint">导致图形发生形变的点</param>
        /// <param name="sender">导致点移动的图形</param>
        public virtual void InternalPrepare(PointBase movePoint, ShapeBase sender = null)
        {
            if (InPoints != null)
            {
                if (IsTop)
                {
                    if (movePoint != null && movePoint.Movable)
                    {
                        foreach (PointBase item in InPoints)
                        {
                            if (item == null || item == movePoint || item.OutHosts == null || item.OutHosts.Length == 0) continue;
                            if (Settings.Instance.AutoFixAbsorbedPoints && PointsHaveConstraint)
                            {
                                item.TempMoveLock = true;
                            }
                            foreach (LineBase line in item.OutHosts)
                            {
                                line.ForEachPoint((PointBase pt) =>
                                {
                                    if (movePoint != pt && InPoints.Contains(pt))
                                    {
                                        pt.TempMoveLock = true;
                                    }
                                    return true;
                                });
                            }
                        }
                        int movCnt = CountMovablePoints(true);
                        if (movCnt == InPoints.Length - 1 && movCnt > 1)
                        {
                            controlPointOriginalCoordinate = movePoint.ToPointD();
                            controlledInPointsOriginalInfo = new KeyValuePair<PointD, double>[movCnt - 1];
                            double len = controlPointOriginalCoordinate.DistanceTo(fixedPoints[0].ToPointD());
                            int i = 0;
                            foreach (PointBase item in InPoints)
                            {
                                if (item != null && item.Movable && item != movePoint)
                                {
                                    controlledInPointsOriginalInfo[i] = new KeyValuePair<PointD, double>(item.ToPointD(),
                                        fixedPoints[0].DistanceTo(item) / len);
                                    i++;
                                }
                            }
                        }
                    }
                    PrepareMove();
                }
            }
        }
        public virtual void ExternalPrepare(PointBase movePoint, ShapeBase sender = null)
        {
            //foreach (ComponentManagerBase item in ComponentManagers)
            //{
            //    item.PointMove_Prepare(movePoint);
            //}
            foreach (LineBase item in OutChildren_Line)
            {
                item.InternalPrepare(movePoint, this);
            }
            foreach (PointBase item in OutChildren_Point)
            {
                item.PrepareMove(this);
            }
        }
        public virtual PointD GetOutFreePointRecommendNewLocation(PointBase pt, bool getXY = true)
        {
            return getXY ? pt.NewXY : pt.NewPxy;
        }
        protected override HashSet<Base> getRelatedObjects()
        {
            HashSet<Base> set = base.getRelatedObjects();
            set.AddRange(OutChildren_Line);
            set.AddRange(OutChildren_Point);
            return set;
        }
        public override void Delete(bool recoverable = false)
        {
            base.Delete(recoverable);
            if (!recoverable)
            {
                if (InPoints != null)
                {
                    for (int i = 0; i < InPoints.Length; i++)
                    {
                        SetPoint(i, null);
                    }
                }
            }
            foreach (PointBase item in OutChildren_Point)
            {
                if (item.ComponentManager == null)
                {
                    if (item.ObjectState == ObjectState.Normal)
                    {
                        item.Delete(recoverable);
                    }
                }
            }
            foreach (LineBase item in OutChildren_Line)
            {
                if (item.ComponentManager == null)
                {
                    if (item.ObjectState == ObjectState.Normal)
                    {
                        item.Delete(recoverable);
                    }
                }
            }
        }
        //public override void Recover(object arg)
        //{
        //    foreach (LineBase item in OutChildren_Line)
        //    {
        //        item.Recover(arg);
        //    }
        //    foreach (RPoint item in OutChildren_Point)
        //    {
        //        item.Recover(arg);
        //    }
        //    base.Recover(arg);
        //}
        public override void PaintVirtualObjects(Graphics gra)
        {
            gra.DrawPoint(SpareCoordinate_);
            base.PaintVirtualObjects(gra);
        }
        protected virtual void PointMoving_AsOutChildrenLimit(PointBase movePoint)
        {

        }
        public void PointMoving_OutChildrenLimit(PointBase movePoint)
        {
            foreach (LineBase line in OutChildren_Line)
            {
                line.PointMoving_AsOutChildrenLimit(movePoint);
                foreach (ComponentManagerBase mgr in line.ComponentManagers)
                {
                    mgr.PointMoving_ManagedComponentsLimit(line);
                }
                line.PointMoving_OutChildrenLimit(movePoint);
            }
            List<PointBase> needMove = null;
            foreach (PointBase item in OutChildren_Point)
            {
                if (item.ComponentManager == null)
                {
                    if (item.ToolType == ToolTypeName.zbd && !item.Movable)
                    {
                        throw new PointMovingException(this, string.Format(Resources.Translate("FixedAndCannotMove"), item.Name));
                    }
                    else
                    {
                        if (!Pad.PointFlags.Contains(item))
                        {
                            //IFreePoint free = item as IFreePoint;
                            //if (free != null)
                            //{
                            //    free.CheckOutHosts = false;
                            //}
                            //item.MoveFixed = true;
                            if (needMove == null)
                            {
                                needMove = new List<PointBase>();
                            }
                            needMove.Add(item);
                            item.NewPxy = item.GetNewLocationAfterOutHostChanged();
                        }
                    }
                }
            }
            if (needMove != null)
            {
                foreach (PointBase item in needMove)
                {
                    try
                    {
                        item.Move(item.NewPxy, item is ConstructedPointBase);
                    }
                    finally
                    {
                        //if (free != null)
                        //{
                        //    free.CheckOutHosts = true;
                        //}
                        //item.MoveFixed = false;
                    }
                }
            }
        }
        /// <summary>
        /// 当图形完成形变后的任务
        /// </summary>
        /// <param name="movedPoint">导致图形发生形变的点</param>
        /// <param name="sender">导致点移动的图形</param>
        public virtual void InternalClean(PointBase movedPoint, ShapeBase sender = null)
        {
            if (InPoints != null)
            {
                if (IsTop)
                {
                    if (movedPoint != null && movedPoint.Movable)
                    {
                        foreach (PointBase item in InPoints)
                        {
                            if (item == null || item == movedPoint || item.OutHosts == null || item.OutHosts.Length == 0) continue;
                            if (Settings.Instance.AutoFixAbsorbedPoints && PointsHaveConstraint)
                            {
                                item.TempMoveLock = false;
                            }
                            foreach (LineBase line in item.OutHosts)
                            {
                                line.ForEachPoint((PointBase pt) =>
                                {
                                    if (movedPoint != pt && InPoints.Contains(pt))
                                    {
                                        pt.TempMoveLock = false;
                                    }
                                    return true;
                                });
                            }
                        }
                        fixedPoints = null;
                        controlPointOriginalCoordinate = PointD.Empty;
                        controlledInPointsOriginalInfo = null;
                    }
                    CleanMove();
                }
            }
        }
        public virtual void ExternalClean(PointBase movedPoint, ShapeBase sender = null)
        {
            //foreach (ComponentManagerBase item in ComponentManagers)
            //{
            //    item.PointMoved_Clean(movedPoint);
            //}
            foreach (LineBase item in OutChildren_Line)
            {
                item.InternalClean(movedPoint, this);
            }
            foreach (PointBase item in OutChildren_Point)
            {
                item.CleanMove(this);
            }
        }
        //public virtual PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        //{
        //    reverse = false;
        //    return currentLocation;
        //}
        protected virtual string getFormula()
        {
            return string.Empty;
        }
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (FormulaDisplayer.InRegion(Plocation))
            {
                res.Add(FormulaDisplayer);
            }
            return res.ToArray();
        }
        public override void ComponentBuilding(Base component)
        {
            formula = Utility.DecoratePolynomial(Utility.StandardizePolynomial(getFormula()));
            base.ComponentBuilding(component);
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                FormulaDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public override bool Track_
        {
            get
            {
                return base.Track_;
            }

            set
            {
                base.Track_ = value;
                InChildrenIterator((ShapeBase obj) => 
                {
                    obj.Track_ = Track_;
                    return true;
                });
            }
        }
        public override void RefreshPropertyDisplayersText()
        {
            FormulaDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override void RefreshPropertyDisplayersLocation()
        {
            FormulaDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override bool Movable
        {
            get
            {
                if (ObjectState == ObjectState.Deleted) return true;
                if (InPoints != null)
                {
                    foreach (PointBase item in InPoints)
                    {
                        if (item != null)
                        {
                            if (!item.Movable)
                            {
                                return false;
                            }
                        }
                    }
                    return true;
                }
                return base.Movable;
            }
        }
        public override bool PrepareMove(ShapeBase sender = null)
        {
            //不能用子元件遍历法
            if (InPoints == null)
            {
                if (Pad.LineFlags.Add(this))
                {
                    InternalPrepare(null, sender);
                }
            }
            else
            {
                bool flag = false;
                foreach (PointBase item in InPoints)
                {
                    if (item == null) continue;
                    if (item.Movable)
                    {
                        if (item.PrepareMove(this))
                        {
                            flag = true;
                        }
                    }
                }
                //foreach (PointBase item in Pad.TempMoveLockedPoints)
                //{
                //    item.TempMoveLock = false;
                //}
                //Pad.TempMoveLockedPoints.Clear();
                if (!flag)
                {
                    if (Pad.LineFlags.Add(this))
                    {
                        InternalPrepare(null, sender);
                    }
                }
            }
            return true;
            //InChildrenIterator((ShapeBase obj) =>
            //{
            //    if (obj.Movable)
            //    {
            //        obj.PrepareMove(this);
            //    }
            //    return true;
            //});
        }
        public virtual bool AllowSwitchPointType(PointBase pt)
        {
            return true;
        }
        public override void BeforePasting(Base src)
        {
            LineBase line = src as LineBase;
            if (line != null)
            {
                OrderIndex = line.OrderIndex;
            }
            base.BeforePasting(src);
        }
        public override bool CleanMove(ShapeBase sender = null)
        {
            if (InPoints == null)
            {
                if (Pad.LineFlags.Add(this))
                {
                    InternalClean(null, sender);
                }
            }
            else
            {
                bool flag = false;
                foreach (PointBase item in InPoints)
                {
                    if (item == null) continue;
                    if (item.Movable)
                    {
                        if (item.CleanMove(this))
                        {
                            flag = true;
                        }
                    }
                }
                //foreach (PointBase item in Pad.TempMoveLockedPoints)
                //{
                //    item.TempMoveLock = false;
                //}
                //Pad.TempMoveLockedPoints.Clear();
                if (!flag)
                {
                    if (Pad.LineFlags.Add(this))
                    {
                        InternalClean(null, sender);
                    }
                }
            }
            return true;
            //InChildrenIterator((ShapeBase obj) =>
            //{
            //    if (obj.Movable)
            //    {
            //        obj.CleanMove(this);
            //    }
            //    return true;
            //});
        }
        public override void Move(PointD offset, bool isCompulsive = false, Base sender = null)
        {
            if (InPoints == null)
            {
                if (Pad.LineFlags.Add(this))
                {
                    ExternalObjectMove(null, sender);
                }
            }
            else
            {
                bool flag = false;
                foreach (PointBase item in InPoints)
                {
                    if (item == null) continue;
                    if (item.Movable)
                    {
                        item.Move(item.NewPxy + offset, false, this);
                        flag = true;
                    }
                }
                //foreach (PointBase item in Pad.TempMoveLockedPoints)
                //{
                //    item.TempMoveLock = false;
                //}
                //Pad.TempMoveLockedPoints.Clear();
                if (!flag)
                {
                    if (Pad.LineFlags.Add(this))
                    {
                        ExternalObjectMove(null, sender);
                    }
                }
            }
        }
        public override void ComponentChanged(ShapeBase component)
        {
            formula = Utility.DecoratePolynomial(Utility.StandardizePolynomial(getFormula()));
            foreach (PointBase pt in OutChildren_Point)
            {
                pt.ComponentChanged(this);
            }
            foreach (LineBase item in OutChildren_Line)
            {
                item.ComponentChanged(this);
            }
            base.ComponentChanged(component);
        }
        public virtual void SetPoint(int index, PointBase value)
        {
            if (InPoints != null)
            {
                if (value != null)
                {
                    if (InPoints.Contains(value))
                    {
                        throw new ComponentCreatingException(this, Resources.Translate("SamePointReference"));
                    }
                }
                if (index < InPoints.Length && index >= 0)
                {
                    InPoints[index]?.InHosts.Remove(this);
                    InPoints[index] = value;
                    InPoints[index]?.InHosts.Add(this);
                }
            }
        }
        public virtual void ChangePoint(PointBase oldPoint, PointBase newPoint)
        {
            if (InPoints == null) return;
            for (int i = 0; i < InPoints.Length; i++)
            {
                if (ReferenceEquals(InPoints[i], oldPoint))
                {
                    SetPoint(i, newPoint);
                    break;
                }
            }
        }
        public override ShapeBase CheckRepeat()
        {
            if (InPoints != null)
            {
                //IEnumerable<LineBase> common = InPoints[0].InHosts;
                //for (int i = 1; i < InPoints.Length; i++)
                //{
                //    common = common.Intersect(InPoints[i].InHosts);
                //}
                //foreach (LineBase item in common)
                //{
                //    if (item != this && !item.InHosts.Contains(this))
                //    {
                //        return item;
                //    }
                //}
            }
            return base.CheckRepeat();
        }
        public override Base[] CheckNearBy(PointD Plocation)
        {
            if (ObjectState == ObjectState.Normal && Visible && IsEntity)
            {
                PointD? tmp = PSkeleton.GetNearestPoint(Plocation);
                if (tmp.HasValue && tmp.Value.DistanceTo(Plocation) <= Settings.Instance.LineAbsorb + LineWidth.Value / 2)
                {
                    HashSet<Base> objs = new HashSet<Base>(base.CheckNearBy(Plocation));
                    AttachedLocation = tmp.Value;
                    objs.Add(this);
                    return objs.ToArray();
                }
            }
            return base.CheckNearBy(Plocation);
        }
        public override bool LocationUsed(IList<PointD> locations)
        {
            if (InPoints != null)
            {
                for (int i = 0; i < InPoints.Length; i++)
                {
                    if (!InPoints[i].ToPointD().AlmostEquals(locations[i], false))
                    {
                        return false;
                    }
                }
                return true;
            }
            return base.LocationUsed(locations);
        }
        public PointD[] GetPointLocations()
        {
            if (InPoints == null) return null;
            PointD[] res = new PointD[InPoints.Length];
            for (int i = 0; i < res.Length; i++)
            {
                res[i] = InPoints[i].ToPointD();
            }
            return res;
        }
        public override object GetCopyInfo()
        {
            if (InPoints == null) return null;
            PointCreatingArgs[] res = new PointCreatingArgs[InPoints.Length];
            for (int i = 0; i < res.Length; i++)
            {
                res[i] = new PointCreatingArgs(InPoints[i].ToPointD(), false, InPoints[i].ToolType == ToolTypeName.zbd);
            }
            return res;
        }
        public override void AutoCreate(object createInfo, bool tryOffset = false)
        {
            IList<PointCreatingArgs> args = createInfo as IList<PointCreatingArgs>;
            if (args == null) return;
            int len = ClickTimes;
            switch (ToolType)
            {
                case ToolTypeName.rydbx:
                    if (args.Count < 2)
                    {
                        throw new ArgumentException("AutoCreateTooFewArguments");
                    }
                    else
                    {
                        len = args.Count;
                    }
                    break;
                default:
                    if (args.Count < len)
                    {
                        throw new ArgumentException("AutoCreateTooFewArguments");
                    }
                    break;
            }
            if (tryOffset)
            {
                Pad.CopyOffset(ToolType, args);
            }
            Pad.HostObj = this;
            PointAbsorb bkp = Pad.PointAbsorbMode;
            ClickOrder(args, len, (PointCreatingArgs arg) =>
            {
                simulateClick(arg);
                return !IsCreationCompleted;
            });
            if (!IsCreationCompleted)
            {
                simulateClick(args[0]);
            }
            Pad.ClearAbsorbedObjects();
            Pad.PointAbsorbMode = bkp;
        }
        protected virtual void ClickOrder(IList<PointCreatingArgs> args, int clickTimes, Predicate<PointCreatingArgs> act)
        {
            if (act == null) return;
            for (int i = 0; i < clickTimes; i++)
            {
                if (!act(args[i]))
                {
                    break;
                }
            }
        }
    }
}
